// Class include
#include "CSocketReader.hpp"

// System includes
#include <netinet/in.h>
#include <unistd.h>     // For sleep
#include <stdio.h>

// Other includes
#include "CTrace.hpp"
#include "TypesCommands.hpp"

// Forward declaration includes
#include "CCommand.hpp"
#include "CObserver.hpp"

CSocketReader::CSocketReader(const int port, CObserver* pObserver)
: mPort(port)
, mSock(0)
{
   addObserver(pObserver);
}

CSocketReader::~CSocketReader()
{
}

void CSocketReader::start()
{
   int listener = socket(AF_INET, SOCK_STREAM, 0);
   if (listener < 0)
   {
      TRC_ERROR("Unable to create socket");
   }

   struct sockaddr_in addr;    
   addr.sin_family = AF_INET;
   addr.sin_port = htons(mPort);
   addr.sin_addr.s_addr = htonl(INADDR_ANY);
   
   if (bind(listener, (struct sockaddr *)&addr, sizeof(addr)) < 0)
   {
      TRC_ERROR("Unable to bind");
      return;
   }

   listen(listener, 1);
 
   while(1)
   {
      TRC_DEBUG("Waiting for incoming connection on port %d", mPort);
        
      mSock = accept(listener, NULL, NULL);
      if (mSock < 0)
      {
         TRC_ERROR("Unable to accept");
         return;
      }

      TRC_DEBUG("Accepted incoming connection on port %d", mPort);

      while(1)
      {
         int bytesRead;
         unsigned char buf[COMMAND_LENGTH];

         bytesRead = recv(mSock, buf, COMMAND_LENGTH, MSG_WAITALL);
         
         if (bytesRead <= 0) break;
         if (bytesRead != COMMAND_LENGTH)
         {
            TRC_WARNING("Something is wrong while reading command from socket. Expected %d bytes, but read %d bytes", COMMAND_LENGTH, bytesRead);
            for (int i=0; i<bytesRead; i++) printf("[%d] = %d ", i, buf[i]); printf("\n");
            break;
         }

         // TODO: create command object. Put command to queue
         CCommand cmd(COMMAND_SOURCE_SOCKET, buf);
         TRC_DEBUG("Read command from socket: %s, value=%d", toString(cmd.getType()), cmd.getValue32());
         for (int i=0; i<bytesRead; i++) printf("[%d] = %d ", i, buf[i]); printf("\n");

         if (COMMAND_INVALID != cmd.getType())
         {
            notifyObservers(cmd);
         }
      }
        
      TRC_DEBUG("Closed connection on port %d", mPort);
    
      close(mSock);
      mSock = 0;
   }
    
   return;
}

void CSocketReader::sendCommand(CCommand cmd)
{
   if (false == isConnected())
   {
      TRC_WARNING("Socket is closed. Skipping command");
      return;
   }

   unsigned char* pRawBuffer = new unsigned char[COMMAND_LENGTH];
   cmd.getRawBuffer(pRawBuffer);

   int bytesSent = send(mSock, pRawBuffer, COMMAND_LENGTH, 0);

   if (bytesSent != COMMAND_LENGTH)
   {
      TRC_WARNING("Not all bytes where sent: %d instead of %d", bytesSent, COMMAND_LENGTH);
   }

   delete[] pRawBuffer;
}

bool CSocketReader::isConnected()
{
   return (mSock != 0);
}